/* ------------------ test method ------------------------------- */
#include <stdio.h>
#include "testhelp.h"
#include "limits.h"

#define UNUSED(x) (void)(x)
/*test sds append value in bits */
int sdsTest(void) {
  sds s = sdsempty();
  uint32_t length = 0;
  s = sdscatbits(s, 1, 1, &length);
  test_cond("One Bit", length==1 && sdslen(s)==1 && '\x80'==s[0]);

  sdsfree(s);
  s = sdsempty();
  length = 0;
  for (int i = 0; i < 16; i++) {
    s = sdscatbits(s, 1, 1, &length);
  }
  test_cond("Write Ones", length==16 && sdslen(s)==2 && '\xFF'==s[0] && '\xFF'==s[1]);

  uint64_t bitPos = 0;
  for (int i = 0; i < 16; i++) {
    uint64_t v = sdsreadbits(s, &bitPos, 1);
    char buf[20] = { 0 };
    sprintf(buf, "Read Ones: %d", i);
    test_cond(buf, 1==v);
  }


  sdsfree(s);
  s = sdsempty();
  length = 0;
  uint32_t value = 0xFACEB00C;
  s = sdscatbits(s, value, 32, &length);
  test_cond("Write Facebook", length==32 && sdslen(s)==4 &&
          '\xFA'==s[0] &&
          '\xCE'==s[1] &&
          '\xB0'==s[2] &&
          '\x0C'==s[3]);

  bitPos = 0;
  uint64_t v1 = sdsreadbits(s, &bitPos, 16);
  test_cond("Read Facebook: 0", 0xFACE==v1 && 16==bitPos);
  uint64_t v2 = sdsreadbits(s, &bitPos, 16);
  test_cond("Read Facebook: 1", 0xB00C==v2 && 32==bitPos);

  sdsfree(s);
  s = sdsempty();
  length = 0;
  s = sdscatbits(s, 11, 6, &length);
  s = sdscatbits(s, 12, 6, &length);
  s = sdscatbits(s, 13, 6, &length);
  s = sdscatbits(s, 14, 6, &length);

  test_cond("Write SmallChunks", length==24 && sdslen(s)==3 &&
          '\x2C'==s[0] &&
          '\xC3'==s[1] &&
          '\x4E'==s[2]);

  bitPos = 0;
  uint64_t v = sdsreadbits(s, &bitPos, 6);
  test_cond("Read SmallChunks 0", 11==v);
  v = sdsreadbits(s, &bitPos, 6);
  test_cond("Read SmallChunks 1", 12==v);
  v = sdsreadbits(s, &bitPos, 6);
  test_cond("Read SmallChunks 2", 13==v);
  v = sdsreadbits(s, &bitPos, 6);
  test_cond("Read SmallChunks 3", 14==v);

  sdsfree(s);
  s = sdsempty();
  length = 0;

  s = sdscatbits(s, 511, 9, &length);
  s = sdscatbits(s, 512, 10, &length);
  s = sdscatbits(s, 5, 3, &length);
  s = sdscatbits(s, 511, 9, &length);
  s = sdscatbits(s, 512, 10, &length);
  s = sdscatbits(s, 5, 3, &length);

  test_cond("Write BigAndSmallChunks", length==44 && sdslen(s)==6 &&
          '\xFF'==s[0] &&
          '\xC0'==s[1] &&
          '\x17'==s[2] &&
          '\xFF'==s[3] &&
          '\x00'==s[4] &&
          '\x50'==s[5]);

  bitPos = 0;
  v = sdsreadbits(s, &bitPos, 9);
  test_cond("Read BigAndSmallChunks 0", 511==v);
  v = sdsreadbits(s, &bitPos, 10);
  test_cond("Read BigAndSmallChunks 1", 512==v);
  v = sdsreadbits(s, &bitPos, 3);
  test_cond("Read BigAndSmallChunks 2", 5==v);
  v = sdsreadbits(s, &bitPos, 9);
  test_cond("Read BigAndSmallChunks 3", 511==v);
  v = sdsreadbits(s, &bitPos, 10);
  test_cond("Read BigAndSmallChunks 4", 512==v);
  v = sdsreadbits(s, &bitPos, 3);
  test_cond("Read BigAndSmallChunks 5", 5==v);

  test_report();
  return 0;
}

int tsdbTest(void) {
  tstream *ts = NULL;
  ts = tstreamCreate();
  test_cond("Create a new tstream",
            ts != NULL && ts->prev_timestamp == 0 && sdslen(ts->data)==0);

  ts->prev_timestamp = 1;
  ts->prev_value = 2;
  sdscat(ts->data, "test");
  tstreamReset(ts);
  test_cond("reset tstream",
            ts != NULL && ts->prev_value == 0 && sdslen(ts->data)==0);

  tstreamRelease(ts);
  ts = tstreamCreate();

  tstreamAppend(ts, 0.0, 5.0, 0);
  tstreamAppend(ts, 60.0, 6.0, 0);
  tstreamAppend(ts, 120.0, 7.0, 0);
  tstreamAppend(ts, 180.0, 7.0, 0);
  tstreamAppend(ts, 240.0, 8.0, 0);
  tstreamAppend(ts, 300.0, 0.3333, 0);

  tsiter iter;
  tstreamInitIterator(&iter);
  tvalue tv;
  tstreamNextValue(&iter, ts, &tv);
  test_cond("Insert and read 0", 0.0==tv.timestamp && 5.0==tv.value);
  tstreamNextValue(&iter, ts, &tv);
  test_cond("Insert and read 1", 60.0==tv.timestamp && 6.0==tv.value);
  tstreamNextValue(&iter, ts, &tv);
  test_cond("Insert and read 2", 120.0==tv.timestamp && 7.0==tv.value);
  tstreamNextValue(&iter, ts, &tv);
  test_cond("Insert and read 3", 180.0==tv.timestamp && 7.0==tv.value);
  tstreamNextValue(&iter, ts, &tv);
  test_cond("Insert and read 4", 240.0==tv.timestamp && 8.0==tv.value);
  tstreamNextValue(&iter, ts, &tv);
  test_cond("Insert and read 5", 300.0==tv.timestamp && 0.3333==tv.value);

  tstreamReset(ts);
  tstreamInitIterator(&iter);
  tstreamAppend(ts, 5, 1, 0);
  tstreamAppend(ts, 6, 1, 1);
  tstreamAppend(ts, 7, 1, 1);

  tstreamNextValue(&iter, ts, &tv);
  test_cond("InsertAndReadConsecutiveTimestampValues 0", 5==tv.timestamp);
  tstreamNextValue(&iter, ts, &tv);
  test_cond("InsertAndReadConsecutiveTimestampValues 1", 6==tv.timestamp);
  tstreamNextValue(&iter, ts, &tv);
  test_cond("InsertAndReadConsecutiveTimestampValues 2", 7==tv.timestamp);

  tstreamReset(ts);
  tstreamInitIterator(&iter);
  tstreamAppend(ts, 5, 1, 1);
  tstreamAppend(ts, 60000000, 1, 1);
  tstreamAppend(ts, 60000007, 1, 1);
  tstreamAppend(ts, 1234567890, 1, 1);
  tstreamAppend(ts, 1234567891, 1, 1);
  tstreamAppend(ts, 2000000000, 1, 1);

  tstreamNextValue(&iter, ts, &tv);
  test_cond("InsertAndReadHugeAndSmallDifferencesInTimestamps 0", 5==tv.timestamp);
  tstreamNextValue(&iter, ts, &tv);
  test_cond("InsertAndReadHugeAndSmallDifferencesInTimestamps 1", 60000000==tv.timestamp);
  tstreamNextValue(&iter, ts, &tv);
  test_cond("InsertAndReadHugeAndSmallDifferencesInTimestamps 2", 60000007==tv.timestamp);
  tstreamNextValue(&iter, ts, &tv);
  test_cond("InsertAndReadHugeAndSmallDifferencesInTimestamps 3", 1234567890==tv.timestamp);
  tstreamNextValue(&iter, ts, &tv);
  test_cond("InsertAndReadHugeAndSmallDifferencesInTimestamps 4", 1234567891==tv.timestamp);
  tstreamNextValue(&iter, ts, &tv);
  test_cond("InsertAndReadHugeAndSmallDifferencesInTimestamps 5", 2000000000==tv.timestamp);

  tstreamReset(ts);
  tstreamInitIterator(&iter);
  tstreamAppend(ts, 100, 1, 60);
  tstreamAppend(ts, 160, 1, 60);
  test_cond("SpammyTimeStream 0", tstreamAppend(ts, 190, 1, 60) == C_ERR);
  test_cond("SpammyTimeStream 1", tstreamAppend(ts, 220, 1, 60) == C_OK);
  test_cond("SpammyTimeStream 2", tstreamAppend(ts, 160, 1, 60) == C_ERR);

  tstreamReset(ts);
  tstreamInitIterator(&iter);
  tstreamAppend(ts, 1, 1, 0);
  tstreamAppend(ts, 2, 1, 1);

  test_cond("HasNext 0", tstreamHasNext(&iter, ts) == C_OK);
  tstreamNextValue(&iter, ts, &tv);
  tstreamNextValue(&iter, ts, &tv);
  test_cond("HasNext 1", tstreamHasNext(&iter, ts) == C_ERR);
  test_cond("HasNext 2", tstreamNextValue(&iter, ts, &tv) == C_ERR);

  // test read data
  tstreamReset(ts);

  tstreamAppend(ts, 0.0, 5.0, 0);
  tstreamAppend(ts, 60.0, 6.0, 0);
  tstreamAppend(ts, 120.0, 7.0, 0);
  tstreamAppend(ts, 180.0, 7.0, 0);
  tstreamAppend(ts, 240.0, 8.0, 0);
  tstreamAppend(ts, 300.0, 0.3333, 0);

  tstream *newts = tstreamCreate();
  tstreamReadData(newts, ts->data);
  test_cond("tstreamReadData first_timestamp", newts->first_timestamp == ts->first_timestamp);
  test_cond("tstreamReadData prev_timestamp", newts->prev_timestamp == ts->prev_timestamp);
  test_cond("tstreamReadData prev_timestamp_delta", newts->prev_timestamp_delta == ts->prev_timestamp_delta);
  test_cond("tstreamReadData prev_value", newts->prev_value == ts->prev_value);
  test_cond("tstreamReadData prev_value_leading_zeros", newts->prev_value_leading_zeros == ts->prev_value_leading_zeros);
  test_cond("tstreamReadData prev_value_trailing_zeros", newts->prev_value_trailing_zeros == ts->prev_value_trailing_zeros);
  test_cond("tstreamReadData num_values", newts->num_values == ts->num_values);
  test_cond("tstreamReadData num_bits", newts->num_bits == ts->num_bits);
  test_report();
	return 0;
}

int main(void) {
  printf("--------------------------------------------------------\n");
  printf("--------------------------------------------------------\n");
  printf(">>> Begin sds bits operation test\n");
  sdsTest();
  printf("--------------------------------------------------------\n");
  printf("--------------------------------------------------------\n");
	printf(">>> Begin tsdb unit test\n");
  return tsdbTest();
}
